package com.docmgmt.mvp.ragflow.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import java.util.List;

/**
 * AI 问答响应 DTO
 * 
 * 用于接收 RAGFlow 的 POST /api/v1/chats/{chat_id}/completions 接口返回结果
 * 
 * 使用场景：
 * - 非流式返回：一次性接收完整的 AI 回答和引用来源
 * - 流式返回的最终结果：在所有 token 发送完毕后，返回完整数据
 * 
 * 使用示例：
 * <pre>
 * CompletionResponse response = ragflowClient.sendMessage(chatId, request);
 * System.out.println("AI 回答：" + response.getAnswer());
 * System.out.println("引用来源数量：" + response.getReference().size());
 * </pre>
 * 
 * @author Dora
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CompletionResponse {

    /**
     * AI 生成的完整回答
     * 
     * 这是 AI 助手根据用户问题和知识库内容生成的回答。
     * 
     * 特点：
     * - 可能包含 Markdown 格式（如标题、列表、代码块）
     * - 可能包含引用标记（如 [1]、[2]，对应 reference 列表）
     * - 长度不固定，取决于问题复杂度和 max_tokens 配置
     * 
     * 示例：
     * "RAGFlow 是一个开源的 RAG（检索增强生成）引擎[1]，主要功能包括：
     * 1. 文档解析和分块
     * 2. 向量化存储
     * 3. 智能检索和问答[2]"
     */
    private String answer;

    /**
     * 引用的文档片段列表
     * 
     * 包含 AI 回答时参考的所有文档片段（chunks），用于：
     * - 展示答案的来源，提升可信度
     * - 允许用户点击查看原文
     * - 验证 AI 回答的准确性
     * 
     * 排序规则：
     * - 按相似度从高到低排序
     * - 相似度相同时，按文档创建时间排序
     * 
     * 注意事项：
     * - 如果知识库中没有相关内容，此列表可能为空
     * - 列表长度受 top_n 参数控制（默认 6）
     * - 每个引用都包含完整的文档信息和相似度分数
     */
    private List<ChunkReference> reference;

    /**
     * 会话 ID
     * 
     * 标识当前对话所属的会话。
     * 
     * 行为：
     * - 如果请求时传了 session_id，返回相同的值
     * - 如果请求时未传 session_id，返回新创建的会话 ID
     * 
     * 用途：
     * - 前端保存此 ID，用于后续的多轮对话
     * - 查询对话历史
     * - 管理会话（重命名、删除等）
     */
    @JsonProperty("session_id")
    private String sessionId;

    /**
     * 文档片段引用 DTO
     * 
     * 表示 AI 回答时引用的一个文档片段（chunk）。
     * 
     * 使用示例：
     * <pre>
     * for (ChunkReference ref : response.getReference()) {
     *     System.out.println("文档：" + ref.getDocumentName());
     *     System.out.println("相似度：" + ref.getSimilarity());
     *     System.out.println("内容：" + ref.getContent());
     * }
     * </pre>
     */
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class ChunkReference {

        /**
         * Chunk ID
         * 
         * RAGFlow 中 chunk 的唯一标识符。
         * 
         * 格式：32 位十六进制字符串
         * 示例：chunk_1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p
         * 
         * 用途：
         * - 查询 chunk 的完整信息
         * - 更新 chunk 内容
         * - 删除 chunk
         */
        @JsonProperty("chunk_id")
        private String chunkId;

        /**
         * Chunk 内容
         * 
         * 文档片段的实际文本内容。
         * 
         * 特点：
         * - 长度受分块策略影响（通常 200-1000 字符）
         * - 可能包含 Markdown 格式
         * - 可能被截断（如果原始 chunk 过长）
         * 
         * 注意：
         * - 这是用于展示的内容，可能不是完整的原始 chunk
         * - 如需完整内容，需要通过 chunk_id 查询
         */
        private String content;

        /**
         * 文档 ID
         * 
         * 该 chunk 所属文档的唯一标识符。
         * 
         * 格式：32 位十六进制字符串
         * 示例：doc_1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p
         * 
         * 用途：
         * - 查询文档的完整信息
         * - 下载原始文档
         * - 查看文档的所有 chunks
         */
        @JsonProperty("document_id")
        private String documentId;

        /**
         * 文档名称
         * 
         * 该 chunk 所属文档的文件名。
         * 
         * 示例：
         * - "RAGFlow 介绍.pdf"
         * - "产品需求文档.docx"
         * - "技术架构设计.md"
         * 
         * 用途：
         * - 在前端展示引用来源
         * - 允许用户点击查看原文
         */
        @JsonProperty("document_name")
        private String documentName;

        /**
         * 相似度分数
         * 
         * 表示该 chunk 与用户问题的相关程度。
         * 
         * 取值范围：0.0 - 1.0
         * - 0.0：完全不相关
         * - 0.5：中等相关
         * - 1.0：完全匹配
         * 
         * 计算方式：
         * - 向量相似度（cosine similarity）
         * - 如果启用了 rerank 模型，则为重排序后的分数
         * 
         * 用途：
         * - 排序引用列表
         * - 过滤低相关度的引用
         * - 展示引用的可信度
         * 
         * 注意：
         * - 分数受 embedding 模型影响
         * - 分数 > similarity_threshold 才会被返回
         */
        @DecimalMin(value = "0.0", message = "相似度分数必须 >= 0.0")
        @DecimalMax(value = "1.0", message = "相似度分数必须 <= 1.0")
        private Float similarity;

        /**
         * 在答案中的引用位置
         * 
         * 表示该 chunk 在 AI 回答中被引用的位置（字符索引）。
         * 
         * 格式：整数列表，每两个元素表示一个引用区间 [start, end]
         * 
         * 示例：
         * - [0, 15]：在答案的第 0-15 个字符处引用了此 chunk
         * - [0, 15, 50, 80]：在两个位置引用了此 chunk
         * 
         * 用途：
         * - 在前端高亮显示引用部分
         * - 实现点击引用跳转到来源
         * - 统计引用频率
         * 
         * 注意：
         * - 如果 quote=false，此字段可能为空
         * - 位置索引基于字符数，不是字节数
         */
        private List<Integer> positions;

        /**
         * 获取引用位置的数量
         * 
         * @return 引用次数（positions 列表长度 / 2）
         */
        public int getReferenceCount() {
            return positions != null ? positions.size() / 2 : 0;
        }

        /**
         * 判断是否在指定位置被引用
         * 
         * @param position 字符位置
         * @return 是否在该位置被引用
         */
        public boolean isReferencedAt(int position) {
            if (positions == null || positions.isEmpty()) {
                return false;
            }
            for (int i = 0; i < positions.size(); i += 2) {
                int start = positions.get(i);
                int end = positions.get(i + 1);
                if (position >= start && position <= end) {
                    return true;
                }
            }
            return false;
        }
    }
}

